﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using System.Xml.Linq;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace WebVTT_Converter
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            FileOpenPicker picker = new FileOpenPicker();
            picker.ViewMode = PickerViewMode.List;
            picker.FileTypeFilter.Add(".smi");
            picker.FileTypeFilter.Add(".sami");
            StorageFile file = await picker.PickSingleFileAsync();
            String UnicodeSAMIDocument;
            using (var stream = await file.OpenAsync(FileAccessMode.Read))
            {
                using (DataReader reader = new DataReader(stream))
                {
                    Byte[] ordermarks = new Byte[3];
                    await reader.LoadAsync(3);
                    reader.ReadBytes(ordermarks);
                    if ((ordermarks[0] == 0xFE && ordermarks[1] == 0xFF) || (ordermarks[0] == 0xFF && ordermarks[1] == 0xFE) || (ordermarks[0] == 0xEF && ordermarks[1] == 0xBB && ordermarks[2] == 0xBF))
                    {
                        UnicodeSAMIDocument = await FileIO.ReadTextAsync(file);
                    }
                    else if (stream.Size <= Int32.MaxValue)
                    {
                        Byte[] bytes = new Byte[stream.Size];
                        ordermarks.CopyTo(bytes, 0);
                        Byte[] biggerpiece = new Byte[stream.Size - 3];
                        await reader.LoadAsync((UInt32)stream.Size - 3);
                        reader.ReadBytes(biggerpiece);
                        biggerpiece.CopyTo(bytes, 3);
                        var euckr = System.Text.Encoding.GetEncoding("EUC-KR");
                        var unibytes = System.Text.Encoding.Convert(euckr, System.Text.Encoding.Unicode, bytes);
                        UnicodeSAMIDocument = System.Text.Encoding.Unicode.GetString(unibytes, 0, unibytes.Length);

                        //HTML처럼 문서 읽다가 CSS 부분까지 읽은 뒤에 클래스에서 lang 어트리뷰트 조사해서 인코딩 인식하도록 만들기
                    }
                    else
                    {
                        await new Windows.UI.Popups.MessageDialog("The file is too large").ShowAsync();
                        return;
                    }
                }
            }



            //var doc = new HtmlDocument();
            //HtmlNode node = doc.CreateElement(str);
            //var nodes = node.Descendants().ToArray();
        }


    }

    class ConstructedElement
    {
        public String elementString;
        public XElement completedObject;
        public Boolean isCompleted
        {
            get { return (completedObject != null); }
        }
    }

    class StringConsumer
    {
        String document;
        Int32 position = 0;
        public StringConsumer(String document)
        {
            this.document = document;
        }

        public Nullable<Char> Prove()
        {
            if (document.Length > position)
                return document[position];
            else return null;
        }

        public void Consume()
        {
            position++;
        }

        public Nullable<Char> ProveAsConsume()
        {
            Nullable<Char> c = Prove();
            Consume();
            return c;
        }

        public void Deconsume()
        {
            position--;
        }
    }

    //12.2 Parsing HTML documents
    class SamiDocument
    {
        Stack<Object> emitAcceptor = new Stack<Object>();
        StringConsumer consumer;

        public SamiDocument(String document)
        {
            //12.2.4 token machine
            consumer = new StringConsumer(document);
            XDocument xdocument = new XDocument();
            try
            {
                while (true)
                {
                    Char? c = consumer.ProveAsConsume();
                    switch (c)
                    {
                        case '&':
                            characterReferenceProcessor();
                            break;
                        case '<':
                            break;
                        case '\0':
                            break;
                        case null://eof
                            break;
                        default:
                            break;
                    }
                }
            }
            catch (IndexOutOfRangeException)
            {

            }

            //12.2.3.3 for mis-nested
        }

        public void characterReferenceProcessor()
        {
            ConstructedElement ce = new ConstructedElement();//CE 활용 필요, EOF일 때 및 완료되었을 때 emit할 객체
            ce.elementString += '&';
            Char? c = consumer.ProveAsConsume();
            if (CharCompare(c, '\u0009', '\u000A', '\u000C', '\u0020', '\u003C', '\u0026', null))
            {
                consumer.Deconsume();
                return;
            }
            else if (c == '#')
            {
                if (CharCompare(consumer.Prove(), true, 'x'))
                {
                    Char?[] hexDigits = GetHexDigits();
                    List<Char> charlist = new List<Char>();
                    Char? compare;
                    while (true)
                    {
                        compare = consumer.ProveAsConsume();
                        if (CharCompare(compare, true, hexDigits))
                            charlist.Add(compare.Value);
                        else
                            break;
                    }
                    if (charlist.Count == 0)
                    {
                        //parse error
                    }
                    else if (compare != ';')
                        consumer.Deconsume();
                    //else - parse error
                }
                else
                {
                    //Char[] digits = GetDigits();
                    //while (CharCompare(consumer.Consume(), true, digits)) ;
                    //consumer.Deconsume();
                }
            }
            else
            {

            }
        }

        Char?[] GetHexDigits()
        {
            return new Char?[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        }

        Char?[] GetDigits()
        {
            return new Char?[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
        }

        Boolean IndexedStartsWith(String str, Int32 index, String start)
        {
            for (Int32 i = 0; i < index; i++)
            {
                if (str[i] != start[i])
                    return false;
            }
            return true;
        }

        Boolean IndexedStartsWith(String str, Int32 index, String start, Boolean ignorecase)
        {
            if (ignorecase == false)
                return IndexedStartsWith(str, index, start);
            else
            {
                for (Int32 i = 0; i < index; i++)
                {
                    if (str[i].ToString().ToLower()[0] != start[i].ToString().ToLower()[0])
                        return false;
                }
                return true;
            }
        }

        Boolean CharCompare(Char? a, params Char?[] b)
        {
            foreach (Char comp in b)
            {
                if (a == comp)
                    return true;
            }
            return false;
        }

        Boolean CharCompare(Char? a, Boolean ignorecase, params Char?[] b)
        {
            if (!ignorecase)
                return CharCompare(a, b);
            else
            {
                foreach (Char comp in b)
                {
                    if (a.ToString().ToLower()[0] == comp.ToString().ToLower()[0])
                        return true;
                }
                return false;
            }
        }
    }
}
